#ifndef _CH32V_PWM_H_
#define _CH32V_PWM_H_

#include <CH32V003.h>

#ifndef PWM_DUTY_MAX
#define PWM_DUTY_MAX		100
#endif

typedef enum
{
	TIM1_CH1 = 0,
	TIM1_CH2,
	TIM1_CH3,
	TIM1_CH4,
    
	TIM2_CH1,
    TIM2_CH2,
    TIM2_CH3,
    TIM2_CH4,
} PWM_CHANNEL_Name;

static const uint32_t GET_PWM_TIM[16]={
    TIM1_BASE,  
    TIM1_BASE,  
    TIM1_BASE,  
    TIM1_BASE,   

    TIM2_BASE,  
    TIM2_BASE,  
    TIM2_BASE,  
    TIM2_BASE,
};

//========================================================================
// 描述: PWM初始化.
// 参数: 定时器通道、输出引脚、复用功能、频率、占空比（0-100）%
// 返回: none.
/*
引脚分布如下：
          00:默认映射   01:部分映射   10:部分映射    11:完全映射
TIM1_CH1     PD2           (PC6)        (PD2)         (PC4)
TIM1_CH2     PA1           (PC7)        (PA1)         (PC7)
TIM1_CH3     PC3           (PC0)        (PC3)         (PC5)
TIM1_CH4     PC4           (PD3)        (PC4)         (PD4)

TIM2_CH1     PD4           (PC5)        (PC1)         (PC1)
TIM2_CH2     PD3           (PC2)        (PD3)         (PC7)
TIM2_CH3     PC0           (PD2)        (PC0)         (PD6)
TIM2_CH4     PD7           (PC1)        (PD7)         (PD5)
注意同一组PWM频率一样，占空比可以不一样，注意复用功能组的选择，成对使用，不能单独引脚设置。   
*/
//========================================================================
void PWM_GPIO_Init(TIM_TypeDef* TIMx, PWM_CHANNEL_Name channel, PIN_Name pin)
{
    pinMode(pin,GPIO_Mode_AF_PP);
	if(TIMx == TIM1)
	{
        /*
                       00:默认映射   01:部分映射   10:部分映射    11:完全映射
            TIM1_CH1     PD2           (PC6)        (PD2)         (PC4)
            TIM1_CH2     PA1           (PC7)        (PA1)         (PC7)
            TIM1_CH3     PC3           (PC0)        (PC3)         (PC5)
            TIM1_CH4     PC4           (PD3)        (PC4)         (PD4)
        */
        if(channel == TIM1_CH1){
            switch (pin){
                case PC6:
                    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE );
                    GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM1,ENABLE);
                    break;
                case PC4:
                    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE );
                    GPIO_PinRemapConfig(GPIO_FullRemap_TIM1,ENABLE);
                    break;
                default:
                    break;
            }
        }else if(channel == TIM1_CH2){
            switch (pin){
                case PC7:
                    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE );
                    GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM1,ENABLE);
                    break;
                default:
                    break;
            }
        }else if(channel == TIM1_CH3){
            switch (pin){
                case PC0:
                    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE );
                    GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM1,ENABLE);
                    break;
                case PC5:
                    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE );
                    GPIO_PinRemapConfig(GPIO_FullRemap_TIM1,ENABLE);
                    break;
                default:
                    break;
            }
        }else if(channel == TIM1_CH4){
            switch (pin){
                case PD3:
                    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE );
                    GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM1,ENABLE);
                    break;
                case PD4:
                    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE );
                    GPIO_PinRemapConfig(GPIO_FullRemap_TIM1,ENABLE);
                    break;
                default:
                    break;
            }            
        }
	}
	else if(TIMx == TIM2)
	{
        /*
                      00:默认映射   01:部分映射   10:部分映射    11:完全映射
            TIM2_CH1     PD4           (PC5)        (PC1)         (PC1)
            TIM2_CH2     PD3           (PC2)        (PD3)         (PC7)
            TIM2_CH3     PC0           (PD2)        (PC0)         (PD6)
            TIM2_CH4     PD7           (PC1)        (PD7)         (PD5) 
        */
		RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE );
        if(channel == TIM2_CH1){
            switch (pin){
                case PC5:
                    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE );
                    GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
                    break;
                case PC1:
                    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE );
                    GPIO_PinRemapConfig(GPIO_PartialRemap2_TIM2,ENABLE);
                    break;
                default:
                    break;
            }
        }else if(channel == TIM2_CH2){
            switch (pin){
                case PC2:
                    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE );
                    GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
                    break;
                case PC7:
                    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE );
                    GPIO_PinRemapConfig(GPIO_FullRemap_TIM2,ENABLE);
                    break;
                default:
                    break;
            }
        }else if(channel == TIM2_CH3){
            switch (pin){
                case PD2:
                    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE );
                    GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
                    break;
                case PD6:
                    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE );
                    GPIO_PinRemapConfig(GPIO_FullRemap_TIM2,ENABLE);
                    break;
                default:
                    break;
            }
        }else if(channel == TIM2_CH4){
            switch (pin){
                case PC1:
                    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE );
                    GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
                    break;
                case PD5:
                    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE );
                    GPIO_PinRemapConfig(GPIO_FullRemap_TIM2,ENABLE);
                    break;
                default:
                    break;
            }            
        }
	}else{
        return;
    }
}

void PWM_Init(PWM_CHANNEL_Name channel, PIN_Name pin, uint32_t freq, uint16_t duty)
{
    uint16_t arr,psc;
    TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;

    TIM_TypeDef* TIMx = (TIM_TypeDef*) GET_PWM_TIM[channel];
    if(duty > PWM_DUTY_MAX)	
	{
		duty = PWM_DUTY_MAX;
	}
    // if(channel<3)duty=PWM_DUTY_MAX-duty;
    uint16_t freq_div = ((SystemCoreClock / freq) >> 16);						
	uint16_t period_temp = (SystemCoreClock / freq / (freq_div+1));	
	uint16_t match_temp = (uint16_t)(period_temp*((float)duty/PWM_DUTY_MAX));	
	arr=period_temp;
	psc=freq_div;	

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_TIM1, ENABLE );
    PWM_GPIO_Init(TIMx, channel, pin);

	TIM_TimeBaseInitStructure.TIM_Period = arr;
	TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit( TIMx, &TIM_TimeBaseInitStructure);

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;       //模式1
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse = match_temp;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

    //ch1
    if((channel == TIM1_CH1) || (channel == TIM2_CH1))
    {
        TIM_OC1Init( TIMx, &TIM_OCInitStructure );
        TIM_OC1PreloadConfig( TIMx, TIM_OCPreload_Disable );
    }
    //ch2
    else if((channel == TIM1_CH2) || (channel == TIM2_CH2))
    {
        TIM_OC2Init( TIMx, &TIM_OCInitStructure );
        TIM_OC2PreloadConfig( TIMx, TIM_OCPreload_Disable );
    }
    //ch3
    else if((channel == TIM1_CH3) || (channel == TIM2_CH3))
    {
        TIM_OC3Init( TIMx, &TIM_OCInitStructure );
        TIM_OC3PreloadConfig( TIMx, TIM_OCPreload_Disable );
    }
    //ch4
    else if((channel == TIM1_CH4) || (channel == TIM2_CH4))
    {
        TIM_OC4Init( TIMx, &TIM_OCInitStructure );
        TIM_OC4PreloadConfig( TIMx, TIM_OCPreload_Disable );
    }
	
	TIM_CtrlPWMOutputs(TIMx, ENABLE );
	TIM_ARRPreloadConfig( TIMx, ENABLE );
	TIM_Cmd( TIMx, ENABLE );
}

//========================================================================
// 描述: 更新PWM占空比.
// 参数: 定时器通道、占空比（0-100）%
// 返回: none.
//========================================================================
void PWM_Duty_Updata (PWM_CHANNEL_Name channel, uint16_t duty)
{
	if(duty > PWM_DUTY_MAX)	
	{
		duty = PWM_DUTY_MAX;
	}
	
	TIM_TypeDef* TIMx = (TIM_TypeDef*) GET_PWM_TIM[channel];
															
	uint16_t period_temp = TIMx->ATRLR;											// 获取自动重装载值
	uint16_t match_temp = (uint16_t)(period_temp*((float)duty/PWM_DUTY_MAX));	// 计算占空比

	if((channel == TIM1_CH1) || (channel == TIM2_CH1))
	{
		TIMx->CH1CVR = match_temp;											    // 装载比较值
	}
	//CH2
	else if((channel == TIM1_CH2) || (channel == TIM2_CH2))
	{
		TIMx->CH2CVR = match_temp;											     // 装载比较值
	}
	//CH3
	else if((channel == TIM1_CH3) || (channel == TIM2_CH3))
	{       
		TIMx->CH3CVR = match_temp;											      // 装载比较值
	}
	//CH4
	else if((channel == TIM1_CH4) || (channel == TIM2_CH4))
	{
		TIMx->CH4CVR = match_temp;											      // 装载比较值
	}
}

//========================================================================
// 描述: 更新PWM频率.
// 参数: 定时器通道、频率、占空比（0-100）%
// 返回: none.
//========================================================================
void PWM_Frequency_Updata (PWM_CHANNEL_Name channel, uint32_t freq, uint16_t duty)
{
    uint16_t arr,psc;
    TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TypeDef* TIMx = (TIM_TypeDef*) GET_PWM_TIM[channel];
															
	uint16_t freq_div = ((SystemCoreClock / freq) >> 16);						
	uint16_t period_temp = (SystemCoreClock / freq / (freq_div+1));	
	uint16_t match_temp = (uint16_t)(period_temp*((float)duty/PWM_DUTY_MAX));	
	arr=period_temp;
	psc=freq_div;	

	TIM_TimeBaseInitStructure.TIM_Period = arr;
	TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit( TIMx, &TIM_TimeBaseInitStructure);

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//模式1
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse = match_temp;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

    //ch1
    if((channel == TIM1_CH1) || (channel == TIM2_CH1))
    {
        TIM_OC1Init( TIMx, &TIM_OCInitStructure );
        TIM_OC1PreloadConfig( TIMx, TIM_OCPreload_Disable );
    }
    //ch2
    else if((channel == TIM1_CH2) || (channel == TIM2_CH2))
    {
        TIM_OC2Init( TIMx, &TIM_OCInitStructure );
        TIM_OC2PreloadConfig( TIMx, TIM_OCPreload_Disable );
    }
    //ch3
    else if((channel == TIM1_CH3) || (channel == TIM2_CH3))
    {
        TIM_OC3Init( TIMx, &TIM_OCInitStructure );
        TIM_OC3PreloadConfig( TIMx, TIM_OCPreload_Disable );
    }
    //ch4
    else if((channel == TIM1_CH4) || (channel == TIM2_CH4))
    {
        TIM_OC4Init( TIMx, &TIM_OCInitStructure );
        TIM_OC4PreloadConfig( TIMx, TIM_OCPreload_Disable );
    }
	
	TIM_CtrlPWMOutputs(TIMx, ENABLE );
}

#endif